package com.aube.plugin.episode;
/**
 * Copyright 2010 Eric Taix (eric.taix@gmail.com) Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and limitations under the
 * License.
 */

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.Interpolator;
import android.widget.Scroller;

import com.huyn.baseframework.utils.SysUtil;
import com.huyn.baseframework.utils.Utils;

/**
 * The workspace is a wide area with a infinite number of screens. Each screen
 * contains a view. A workspace is meant to be used with a fixed width only.<br/>
 * <br/>
 * This code has been done by using com.android.launcher.Workspace.java
 */
public class VideoPager extends ViewGroup {

	private static final String TAG = "VideoPager";

	private static final int INVALID_POINTER = -1;

	private int mActivePointerId = INVALID_POINTER;
	private static final int INVALID_SCREEN = -1;

	// The velocity at which a fling gesture will cause us to snap to the next
	// screen
	private static final int SNAP_VELOCITY = 10;

	// the default screen index
	private int defaultScreen;
	// The current screen index
	private int currentScreen;
	// The next screen index
	private int nextScreen = INVALID_SCREEN;

	// The scroller which scroll each view
	private Scroller scroller;
	// A tracker which to calculate the velocity of a mouvement
	private VelocityTracker mVelocityTracker;

	// Tha last known values of X and Y
	private float lastMotionX;
	private float lastMotionY;

	private final static int TOUCH_STATE_REST = 0;
	private final static int TOUCH_STATE_SCROLLING = 1;

	// The current touch state
	private int touchState = TOUCH_STATE_REST;
	// The minimal distance of a touch slop
	private int touchSlop;

	// An internal flag to reset long press when user is scrolling
	private boolean allowLongPress;
	// A flag to know if touch event have to be ignored. Used also in internal
	private boolean locked;

	private WorkspaceOvershootInterpolator mScrollInterpolator;

	private int mMaximumVelocity;

	private int paddingleft;

	private OnPageChangedListener callBack;

	private int childWidth, space;

	private static class WorkspaceOvershootInterpolator implements Interpolator {
		private static final float DEFAULT_TENSION = 1.3f;
		private float mTension;

		public WorkspaceOvershootInterpolator() {
			mTension = DEFAULT_TENSION;
		}

		public void setDistance(int distance) {
			mTension = distance > 0 ? DEFAULT_TENSION / distance
					: DEFAULT_TENSION;
		}

		public void disableSettle() {
			mTension = 0.f;
		}

		public float getInterpolation(float t) {
			// _o(t) = t * t * ((tension + 1) * t + tension)
			// o(t) = _o(t - 1) + 1
			t -= 1.0f;
			return t * t * ((mTension + 1) * t + mTension) + 1.0f;
		}
	}

	public VideoPager(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		defaultScreen = 0;
		initWorkspace(context);
	}

	public VideoPager(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}
	public VideoPager(Context context) {
		this(context, null);
	}

	/**
	 * Initializes various states for this workspace.
	 */
	private void initWorkspace(Context context) {
		mScrollInterpolator = new WorkspaceOvershootInterpolator();
		scroller = new Scroller(getContext(), mScrollInterpolator);
		currentScreen = defaultScreen;

		// Does this do anything for me?
		final ViewConfiguration configuration = ViewConfiguration.get(getContext());
		touchSlop = configuration.getScaledTouchSlop();
		mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
		
		paddingleft = 0;//50;//getContext().getResources().getDimensionPixelSize(R.dimen.cinema_detail_service_padding);

		space = Utils.dip2px(context, 15);//getResources().getDimensionPixelOffset(R.dimen.widget_margin);
		childWidth = SysUtil.getScreenWidth(context)-space*2;
	}

	/**
	 * Set a new distance that a touch can wander before we think the user is
	 * scrolling in pixels slop<br/>
	 * 
	 * @param touchSlopP
	 */
	public void setTouchSlop(int touchSlopP) {
		touchSlop = touchSlopP;
	}

	boolean isDefaultScreenShowing() {
		return currentScreen == defaultScreen;
	}

	/**
	 * Returns the index of the currently displayed screen.
	 * 
	 * @return The index of the currently displayed screen.
	 */
	public int getCurrentScreen() {
		return currentScreen;
	}

	/**
	 * Sets the current screen.
	 * @param theCurrentScreen
	 */
	public void setCurrentScreen(int theCurrentScreen) {
		if (!scroller.isFinished())
			scroller.abortAnimation();
		currentScreen = Math.max(0, Math.min(theCurrentScreen, getChildCount()));
		scrollTo(currentScreen * getItemWidth(), 0);
		Utils.Log("workspace", "setCurrentScreen: width is " + getItemWidth());
		invalidate();
	}

	/**
	 * Shows the default screen (defined by the firstScreen attribute in XML.)
	 */
	void showDefaultScreen() {
		setCurrentScreen(defaultScreen);
	}

	/**
	 * Registers the specified listener on each screen contained in this
	 * workspace.
	 * 
	 * @param l
	 *            The listener used to respond to long clicks.
	 */
	@Override
	public void setOnLongClickListener(OnLongClickListener l) {
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			getChildAt(i).setOnLongClickListener(l);
		}
	}
	
	public int getItemWidth() {
		if(getChildCount() > 0)
			return getChildAt(0).getMeasuredWidth();
		return getWidth();
	}

	@Override
	public void computeScroll() {
		if (scroller.computeScrollOffset()) {
			scrollTo(scroller.getCurrX(), scroller.getCurrY());
//			Log.i("workspace", "scroll to =" + scroller.getCurrX());
			postInvalidate();
		} else if (nextScreen != INVALID_SCREEN) {
			currentScreen = Math.max(0,
					Math.min(nextScreen, getChildCount() - 1));
//			nextScreen = INVALID_SCREEN;
		}
	}

	/**
	 * Measure the workspace AND also children
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int height = 0;
		int mMeasuredWidth = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
		
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			getChildAt(i).measure(mMeasuredWidth, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

			int h = getChildAt(i).getMeasuredHeight();
			if(h > height) height = h;
		}

		heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);

		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}

	private void updateTabIndicator(int whichScreen) {
		if(callBack != null)
			executeOnScrollToScreen(callBack, whichScreen);
	}
	
	public void setOnPageChangedListener(OnPageChangedListener listener) {
		this.callBack = listener;
	}
	
	public int getScreenCount(){
		return this.getChildCount();
	}
	/**
	 * Overrided method to layout child
	 */
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		int childLeft = 0;
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			final View child = getChildAt(i);
			if (child.getVisibility() != View.GONE) {
				final int childWidth = child.getMeasuredWidth();
				child.layout(childLeft + paddingleft, 0, childLeft + paddingleft + childWidth,
						child.getMeasuredHeight());
				childLeft += childWidth;
			}
		}
	}

	@Override
	public boolean dispatchUnhandledMove(View focused, int direction) {
		Utils.Log("workspace","dispatchUnhandledMove:" + direction);
		if (direction == View.FOCUS_LEFT) {
			if (getCurrentScreen() > 0) {
				scrollToScreen(getCurrentScreen() - 1);
				return true;
			}
		} else if (direction == View.FOCUS_RIGHT) {
			if (getCurrentScreen() < getChildCount() - 1) {
				scrollToScreen(getCurrentScreen() + 1);
				return true;
			}
		}
		return super.dispatchUnhandledMove(focused, direction);
	}

	/**
	 * This method JUST determines whether we want to intercept the motion. If
	 * we return true, onTouchEvent will be called and we do the actual
	 * scrolling there.
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		Utils.Log("workspace", "Intercepted a touch event");
		if (locked) {
			return true;
		}

		/*
		 * Shortcut the most recurring case: the user is in the dragging state
		 * and he is moving his finger. We want to intercept this motion.
		 */
		final int action = ev.getAction();
		if ((action == MotionEvent.ACTION_MOVE)
				&& (touchState != TOUCH_STATE_REST)) {
			return true;
		}

		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
		mVelocityTracker.addMovement(ev);

		// switch (action & MotionEvent.ACTION_MASK) {
		switch (action) {
		case MotionEvent.ACTION_MOVE:

			// Log.i("workspace","Intercepted a move event");
			/*
			 * Locally do absolute value. mLastMotionX is set to the y value of
			 * the down event.
			 */
			handleInterceptMove(ev);
			break;

		case MotionEvent.ACTION_DOWN:
			// Remember location of down touch
			final float x1 = ev.getX();
			final float y1 = ev.getY();
			lastMotionX = x1;
			lastMotionY = y1;
			allowLongPress = true;
			mActivePointerId = ev.getPointerId(0);

			/*
			 * If being flinged and user touches the screen, initiate drag;
			 * otherwise don't. mScroller.isFinished should be false when being
			 * flinged.
			 */
			touchState = scroller.isFinished() ? TOUCH_STATE_REST
					: TOUCH_STATE_SCROLLING;
			break;

		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			mActivePointerId = INVALID_POINTER;
			allowLongPress = false;

			mVelocityTracker.recycle();
			mVelocityTracker = null;
			touchState = TOUCH_STATE_REST;
			break;

		case MotionEvent.ACTION_POINTER_UP:
			onSecondaryPointerUp(ev);
			break;
		}

		/*
		 * The only time we want to intercept motion events is if we are in the
		 * drag mode.
		 */
		return touchState != TOUCH_STATE_REST;
	}

	private void handleInterceptMove(MotionEvent ev) {
		final int pointerIndex = ev.findPointerIndex(mActivePointerId);
		final float x = ev.getX(pointerIndex);
		final float y = ev.getY(pointerIndex);
		final int xDiff = (int) Math.abs(x - lastMotionX);
		final int yDiff = (int) Math.abs(y - lastMotionY);
		boolean xMoved = xDiff > touchSlop;
		boolean yMoved = yDiff > touchSlop;

		if (xMoved || yMoved) {
			// Log.i("workspace","Detected move.  Checking to scroll.");
//			if (xMoved && !yMoved) {
		    if (xMoved) {
				// Log.i("workspace","Detected X move.  Scrolling.");
				// Scroll if the user moved far enough along the X axis
				touchState = TOUCH_STATE_SCROLLING;
				lastMotionX = x;
			}
			// Either way, cancel any pending longpress
			if (allowLongPress) {
				allowLongPress = false;
				// Try canceling the long press. It could also have been
				// scheduled
				// by a distant descendant, so use the mAllowLongPress flag to
				// block
				// everything
				final View currentView = getChildAt(currentScreen);
				currentView.cancelLongPress();
			}
		}
	}

	private void onSecondaryPointerUp(MotionEvent ev) {
		final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
		final int pointerId = ev.getPointerId(pointerIndex);
		if (pointerId == mActivePointerId) {
			// This was our active pointer going up. Choose a new
			// active pointer and adjust accordingly.
			// TODO: Make this decision more intelligent.
			final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
			lastMotionX = ev.getX(newPointerIndex);
			lastMotionY = ev.getY(newPointerIndex);
			mActivePointerId = ev.getPointerId(newPointerIndex);
			if (mVelocityTracker != null) {
				mVelocityTracker.clear();
			}
		}
	}

	/**
	 * Track the touch event
	 */
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		Utils.Log("workspace","caught a touch event:" + ev.getAction());
		if (locked) {
			return true;
		}
		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
		mVelocityTracker.addMovement(ev);

		final int action = ev.getAction();
		final float x = ev.getX();

		switch (action) {
		case MotionEvent.ACTION_DOWN:
			// We can still get here even if we returned false from the
			// intercept function.
			// That's the only way we can get a TOUCH_STATE_REST (0) here.
			// That means that our child hasn't handled the event, so we need to
			// Log.i("workspace","caught a down touch event and touchstate =" +
			// touchState);

			if (touchState != TOUCH_STATE_REST) {
				/*
				 * If being flinged and user touches, stop the fling. isFinished
				 * will be false if being flinged.
				 */
				if (!scroller.isFinished()) {
					scroller.abortAnimation();
				}

				// Remember where the motion event started
				lastMotionX = x;
				mActivePointerId = ev.getPointerId(0);
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if (touchState == TOUCH_STATE_SCROLLING) {
				handleScrollMove(ev);
			} else {
				// Log.i("workspace","caught a move touch event but not scrolling");
				// NOTE: We will never hit this case in Android 2.2. This is to
				// fix a 2.1 bug.
				// We need to do the work of interceptTouchEvent here because we
				// don't intercept the move
				// on children who don't scroll.

				Utils.Log("workspace", "handling move from onTouch");

				if (onInterceptTouchEvent(ev)
						&& touchState == TOUCH_STATE_SCROLLING) {
					handleScrollMove(ev);
				}

			}

			break;
		case MotionEvent.ACTION_CANCEL:
//			Log.i("workspace", "caught a cancel touch event");
//			touchState = TOUCH_STATE_REST;
//			mActivePointerId = INVALID_POINTER;
//			break;
		case MotionEvent.ACTION_UP:
			// Log.i("workspace","caught an up touch event");
			if (touchState == TOUCH_STATE_SCROLLING) {
				final VelocityTracker velocityTracker = mVelocityTracker;
				velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
				int velocityX = (int) velocityTracker.getXVelocity();

				if (velocityX > SNAP_VELOCITY && currentScreen > 0) {
					// Fling hard enough to move left
					scrollToScreen(currentScreen - 1);
				} else if (velocityX < -SNAP_VELOCITY
						&& currentScreen < getChildCount() - 1) {
					// Fling hard enough to move right
					scrollToScreen(currentScreen + 1);
				} else {
					snapToDestination();
				}

				if (mVelocityTracker != null) {
					mVelocityTracker.recycle();
					mVelocityTracker = null;
				}
			}
			touchState = TOUCH_STATE_REST;
			mActivePointerId = INVALID_POINTER;
			break;
		case MotionEvent.ACTION_POINTER_UP:
			Utils.Log("workspace", "caught a pointer up touch event");
			onSecondaryPointerUp(ev);
			break;
		}

		return true;
	}

	private void handleScrollMove(MotionEvent ev) {
		// Scroll to follow the motion event
		final int pointerIndex = ev.findPointerIndex(mActivePointerId);
		final float x1 = ev.getX(pointerIndex);
		final int deltaX = (int) (lastMotionX - x1);
		lastMotionX = x1;

		if (deltaX < 0) {
			if (getScrollX() > 0) {
				// Scrollby invalidates automatically
				scrollBy(Math.max(-getScrollX(), deltaX), 0);
			}
		} else if (deltaX > 0) {
			final int availableToScroll = getChildAt(getChildCount() - 1)
					.getRight() - getScrollX() - getWidth();
			Utils.Log("workspace", "last right:" + getChildAt(getChildCount() - 1)
					.getRight() + "_scroll x:" + getScrollX() + " at itemwidth:" + getItemWidth());
			if (availableToScroll > 0) {
				// Scrollby invalidates automatically
				scrollBy(Math.min(availableToScroll, deltaX), 0);
			}
		} else {
			awakenScrollBars();
		}
	}

	/**
	 * Scroll to the appropriated screen depending of the current position
	 */
	private void snapToDestination() {
		final int screenWidth = getItemWidth();
		final int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth;
		Utils.Log("workspace", "snapToDestination");
		scrollToScreen(whichScreen);
	}

	/**
	 * Scroll to a specific screen
	 * 
	 * @param whichScreen
	 */
	public void scrollToScreen(int whichScreen) {
		scrollToScreen(whichScreen, false);
	}

	private void scrollToScreen(int whichScreen, boolean immediate) {
		Utils.Log("workspace", "snapToScreen=" + whichScreen);

		boolean changingScreens = whichScreen != currentScreen;

		nextScreen = whichScreen;

		View focusedChild = getFocusedChild();
		if (focusedChild != null && changingScreens
				&& focusedChild == getChildAt(currentScreen)) {
			focusedChild.clearFocus();
		}

		int newX = whichScreen * getItemWidth();
		if(whichScreen == getChildCount() - 1 && getChildCount() > 1)
			newX -= getWidth()-getItemWidth()-space;
		final int delta = newX - getScrollX();
		Utils.Log("workspace", "newX=" + newX + " scrollX=" + getScrollX() + " delta=" + delta);
//		scroller.startScroll(getScrollX(), 0, delta, 0,
//				immediate ? 0 : Math.abs(delta) * 2);
		scroller.startScroll(getScrollX(), 0, delta, 0,
				immediate ? 0 : 200);
		invalidate();
		updateTabIndicator(whichScreen);
	}

	public void scrollToScreenImmediate(int whichScreen) {
		scrollToScreen(whichScreen, true);
	}

	/**
	 * Return the parceable instance to be saved
	 */
	@Override
	protected Parcelable onSaveInstanceState() {
		final SavedState state = new SavedState(super.onSaveInstanceState());
		state.currentScreen = currentScreen;
		return state;
	}

	/**
	 * Restore the previous saved current screen
	 */
	@Override
	protected void onRestoreInstanceState(Parcelable state) {
		SavedState savedState = (SavedState) state;
		super.onRestoreInstanceState(savedState.getSuperState());
		if (savedState.currentScreen != -1) {
			currentScreen = savedState.currentScreen;
		}
	}

	/**
	 * Scroll to the left right screen
	 */
	public void scrollLeft() {
		if (nextScreen == INVALID_SCREEN && currentScreen > 0
				&& scroller.isFinished()) {
			scrollToScreen(currentScreen - 1);
		}
	}

	/**
	 * Scroll to the next right screen
	 */
	public void scrollRight() {
		if (nextScreen == INVALID_SCREEN && currentScreen < getChildCount() - 1
				&& scroller.isFinished()) {
			scrollToScreen(currentScreen + 1);
		}
	}

	/**
	 * Return the screen's index where a view has been added to.
	 * 
	 * @param v
	 * @return
	 */
	public int getScreenForView(View v) {
		int result = -1;
		if (v != null) {
			ViewParent vp = v.getParent();
			int count = getChildCount();
			for (int i = 0; i < count; i++) {
				if (vp == getChildAt(i)) {
					return i;
				}
			}
		}
		return result;
	}

	/**
	 * Return a view instance according to the tag parameter or null if the view
	 * could not be found
	 * 
	 * @param tag
	 * @return
	 */
	public View getViewForTag(Object tag) {
		int screenCount = getChildCount();
		for (int screen = 0; screen < screenCount; screen++) {
			View child = getChildAt(screen);
			if (child.getTag() == tag) {
				return child;
			}
		}
		return null;
	}

	/**
	 * Unlocks the SlidingDrawer so that touch events are processed.
	 * 
	 * @see #lock()
	 */
	public void unlock() {
		locked = false;
	}

	/**
	 * Locks the SlidingDrawer so that touch events are ignores.
	 * 
	 * @see #unlock()
	 */
	public void lock() {
		locked = true;
	}

	/**
	 * @return True is long presses are still allowed for the current touch
	 */
	public boolean allowLongPress() {
		return allowLongPress;
	}

	/**
	 * Move to the default screen
	 */
	public void moveToDefaultScreen() {
		scrollToScreen(defaultScreen);
		getChildAt(defaultScreen).requestFocus();
	}

	// ========================= INNER CLASSES ==============================

	/**
	 * A SavedState which save and load the current screen
	 */
	public static class SavedState extends BaseSavedState {
		int currentScreen = -1;

		/**
		 * Internal constructor
		 * 
		 * @param superState
		 */
		SavedState(Parcelable superState) {
			super(superState);
		}

		/**
		 * Private constructor
		 * 
		 * @param in
		 */
		private SavedState(Parcel in) {
			super(in);
			currentScreen = in.readInt();
		}

		/**
		 * Save the current screen
		 */
		@Override
		public void writeToParcel(Parcel out, int flags) {
			super.writeToParcel(out, flags);
			out.writeInt(currentScreen);
		}

		/**
		 * Return a Parcelable creator
		 */
		public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
			public SavedState createFromParcel(Parcel in) {
				return new SavedState(in);
			}

			public SavedState[] newArray(int size) {
				return new SavedState[size];
			}
		};
	}

	// Added for "flipper" compatibility
	public int getDisplayedChild() {
		return getCurrentScreen();
	}

	public void setDisplayedChild(int i) {
		// setCurrentScreen(i);
		scrollToScreen(i);
		getChildAt(i).requestFocus();
	}

	public void flipLeft() {
		scrollLeft();
	}

	public void flipRight() {
		scrollRight();
	}

	// ======================== UTILITIES METHODS ==========================

	/**
	 * Return a centered Bitmap
	 * 
	 * @param bitmap
	 * @param width
	 * @param height
	 * @param context
	 * @return
	 */
	static Bitmap centerToFit(Bitmap bitmap, int width, int height,
			Context context) {
		final int bitmapWidth = bitmap.getWidth();
		final int bitmapHeight = bitmap.getHeight();

		if (bitmapWidth < width || bitmapHeight < height) {
			// Normally should get the window_background color of the context
			int color = Integer.valueOf("191919", 16);
			Bitmap centered = Bitmap.createBitmap(bitmapWidth < width ? width
					: bitmapWidth, bitmapHeight < height ? height
					: bitmapHeight, Bitmap.Config.RGB_565);
			Canvas canvas = new Canvas(centered);
			canvas.drawColor(color);
			canvas.drawBitmap(bitmap, (width - bitmapWidth) / 2.0f,
					(height - bitmapHeight) / 2.0f, null);
			bitmap = centered;
		}
		return bitmap;
	}
	public void executeOnScrollToScreen(OnPageChangedListener callBack, int whichScreen){
		callBack.onPageChanged(whichScreen, getChildAt(whichScreen));
	}

	public interface OnPageChangedListener {
		public void onPageChanged(int whichScreen, View view);
	}
	
}